package hivMultiCompartment;

import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;

public abstract class MobileObject extends AgentOnGrid implements Cloneable{
	
	//instance fields
	public int life;
	public int avgLife;
	public int moveInterval;
	public int moveCD;
	
	public boolean alive;
	
	public double probExit; //probability to exit from allowed ports
	public double probEnterLymph;
	public double probEnterThymus;//from blood to thymus. actually accumulative (prob Enter LN + prob Enter Thymus)
	
	//overloading the constructor for different Organs
	public MobileObject() {
		super ();
		initiateObject();
	}
	
	public void initiateObject() {
		moveCD = 0;
		alive = true;
		//System.out.println(probExit);
	}
	
	/**
	 * return true only if cell or virus leaves this organ.
	 * all dead cells/virus will be processed later.
	 */
	public boolean step () {
		boolean toExit = false;
		
		if(!processLife()){
			return true;
		}
		//move agent
		if (moveCD == 0) {
			//only actually move the agent if its 2D grid
			if (Organ2D != null) {
				//in the case of on an spatial Organ: move on grid
				//System.out.println(Organ2D.toString());
				toExit = agentMove();
			}else {
				//in blood: attempts exiting
				double r = Model.getUniformDoubleFromTo(0.0,1.0);
				if(r < probEnterLymph) {
					OrganNS.exitToLN.exitOrgan(this);
					toExit = true;
//					System.out.println("enter LN! " + this.toString());
				}else if(r < probEnterThymus) {
					OrganNS.exitToThymus.exitOrgan(this);
					toExit = true;
//					System.out.println("enter Thymus! " + this.toString());
				}
			}
			resetMoveCD ();
		}
		--moveCD;
		return toExit;
	}
	
	/**
	 * 
	 * @return false if agent is dead
	 */
	public abstract boolean processLife();
	public abstract void statesAndInteractions();
	
	public boolean agentMove(){
		// look around for open spot and pick a random one to move in:
		Point pt = Organ2D.cellSpace.getRandomOpenNeighborLocation(x, y, GlobalConstants.distancePerStep);
		if(pt != null){
			Organ2D.cellSpace.moveObjectTo( (int)pt.getX(), (int)pt.getY(), this );
		}
		boolean exited = tryToExit();
		return exited;
	}
	
	public void agentDie() {
		alive = false;
		setColor(Color.PINK);
	}
	
	public boolean isAlive(){
		return alive;
	}
	
	public void reportAgent(){
		System.out.printf("x=%d,y=%d,life=%d\n",x, y, life);
		ArrayList<Object> neighborCells = Organ2D.cellSpace.getMooreNeighborOfType(x, y, 1, Cell.class);
		System.out.printf("neighbor cell number: %d\n", neighborCells.size());
	}
	
	/**
	 *  reset the cool down time for motion
	 */
	public void resetMoveCD () {
		moveCD = moveInterval;
	}
	
	/**
	 * reset the life for this agent
	 * @param a new life
	 */
	public void resetLife (int a) {
		life = a;
	}
	
	/**
	 * reset the movement interval.
	 * @param i interval
	 */
	public void resetMoveInterval(int i) {
		moveInterval = i;
	}
	
	/**
	 * Agent attempt to exit
	 * @return
	 */
	public boolean tryToExit() {
		boolean result = false;
		//get an exit
		OutputPort p = getAnExit();
		if(p != null) {
			//calculate probability and try to exit
			double r = Model.getUniformDoubleFromTo(0.0,1.0);
			if (r < probExit) {
				result = p.exitOrgan(this);
//				System.out.println("found! " + this.toString());
			}
		}
		return result;
	}
	/**
	 * get an port accessible to this agent for exit
	 * @return
	 */
	public OutputPort getAnExit() {
		OutputPort p = null;
		//System.out.println(this.toString());
		ArrayList<Object> outPortList = Organ2D.cellSpace.getMooreNeighborOfType (x, y, 1, OutputPort.class);
		
		//filter for exit ports allowing this agent
		ArrayList<OutputPort> allowedPortList = new ArrayList<OutputPort>();
		Iterator<Object> oIter = outPortList.iterator();
		while (oIter.hasNext()) {
			OutputPort o = (OutputPort)oIter.next();
			if (o.isExit(this)) {
				allowedPortList.add(o);
			}
		}
		//choose one among allowed exits
		int n = allowedPortList.size();
		if (n > 0) {
			int i = Model.getUniformIntFromTo(0, n - 1);
			p = allowedPortList.get(i);
		}
		return p;
	}
	
	/**
	 * remove object from either a cell list or virus list and move it to somewhere else
	 * need to be called when creating; recruiting; exiting; importing;
	 */
	public abstract void clearMyList();
	
}
